package com.sydsen.imager;

import com.sydsen.imager.elements.Element;
import com.sydsen.imager.elements.Image;
import com.sydsen.imager.painter.IPainter;
import com.sydsen.imager.painter.PainterFactory;
import com.sydsen.imager.processor.IProcessor;
import com.sydsen.imager.processor.ProcessorFactory;
import com.sydsen.imager.util.Base64;
import com.sydsen.imager.util.ImageUtils;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

/**
 * @Auther: syh
 * @Date: 2021/10/29
 * @Description:
 */
public class ImageRender {
    private Canvas canvas; // 画布
    private final List<Element<? extends Element<?>>> elements = new LinkedList<>(); // 图层

    public ImageRender(String imagePath) throws Exception {
        this(ImageUtils.read(imagePath));
    }

    public ImageRender(String imagePath, ImageOptions.PicType picType) throws Exception {
        this(ImageUtils.read(imagePath), picType);
    }

    public ImageRender(InputStream stream) throws Exception {
        this(ImageIO.read(stream));
    }

    public ImageRender(InputStream stream, ImageOptions.PicType picType) throws Exception {
        this(ImageIO.read(stream), picType);
    }

    public ImageRender(BufferedImage image) {
        this(image, ImageOptions.PicType.PNG);
    }

    public ImageRender(BufferedImage image, ImageOptions.PicType picType) {
        this.canvas = new Canvas(image.getWidth(), image.getHeight(), picType);
        this.add(new Image(image));
    }

    public ImageRender setPicType(ImageOptions.PicType picType) {
        this.canvas.setPicType(picType);
        return this;
    }

    public Image getBackground() {
        return (Image)this.elements.get(0);
    }

    public <T extends Element<T>> T add(T t) {
        this.elements.add(t);
        return t;
    }

    public void draw() throws Exception {
        // 初始化画布
        this.canvas.initCanvas();

        // 元素处理
        Iterator<Element<? extends Element<?>>> iter = this.elements.iterator();
        List<Element> finalElement = new LinkedList<>();
        while (iter.hasNext()) {
            Element<? extends Element<?>> element = iter.next();
            IProcessor processor = ProcessorFactory.getProcessor(element);
            processor.process(element, finalElement);
        }

        // 绘图
        for (Element element : finalElement) {
            IPainter painter = PainterFactory.getPainter(element);
            painter.draw(this.canvas, element);
        }

        this.canvas.dispose();
    }


    /*****************对象输出相关方法*******************/

    /**
     * 获取合成后的图片对象
     *
     * @return
     */
    public BufferedImage getBufferedImage() {
        return this.canvas.getImage();
    }

    public byte[] getImageBytes() throws Exception {
        if (getBufferedImage() != null) {
            try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
                ImageIO.write(getBufferedImage(), this.canvas.getPicType().getName(), os);
                return os.toByteArray();
            } catch (Exception e) {
                throw new Exception("执行图片合成失败，无法输出文件流");
            }
        } else {
            throw new Exception("尚未执行图片合成，无法输出文件流");
        }
    }

    /**
     * 获取合成后的图片流
     *
     * @return
     * @throws Exception
     */
    public InputStream getImageStream() throws Exception {
        return new ByteArrayInputStream(getImageBytes());
    }

    public String getImageString() throws Exception {
        return Base64.encode(getImageBytes());
    }

    /**
     * 保存合成后的图片
     *
     * @param filePath 完整保存路径，如 “d://123.jpg”
     */
    public void save(String filePath) throws Exception {
        if (getBufferedImage() != null) {
            File file = new File(filePath);
            if (!file.getParentFile().exists()) {
                file.getParentFile().mkdir();
            }
            ImageIO.write(getBufferedImage(), this.canvas.getPicType().getName(), new File(filePath));
        } else {
            throw new Exception("尚未执行图片合成，无法保存文件");
        }
    }
}
